package pl.streamsoft.currencyApp.services;

import java.math.BigDecimal;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;

import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.criterion.ProjectionList;
import org.hibernate.criterion.Projections;

import pl.streamsoft.currencyApp.model.Currency;
import pl.streamsoft.currencyApp.model.DataBaseConector;

public class DataBaseCurrencyService implements ICurrencyService {

	private final SessionFactory sessionFactory = DataBaseConector.getInstanceOfSesionFactory();

	private Session getSession() {
		return sessionFactory.openSession();
	}
	
	public DataBaseCurrencyService reset(){
		DataBaseConector.reset();
		return this;
	}

	@Override
	public Collection<Currency> getCurrencies(Date dateOfPublication) {

		Session session = getSession();

		try {

			session.beginTransaction();

			Query query = session
					.getNamedQuery("Currency.getCurrenciesByDateOfPublication");
			query.setDate("dateOfPublication", dateOfPublication);
			return query.list();

		} finally {
			session.getTransaction().commit();
			session.close();
		}
	}

	@Override
	public Collection<Currency> getLatestCurrencies() {

		Session session = getSession();
		Date latestDate;

		try {

			session.beginTransaction();

			Criteria criteria = session.createCriteria(Currency.class);
			criteria.setMaxResults(1);
			ProjectionList projectionList = Projections.projectionList();

			projectionList.add(Projections.max("currencyValue.dateOfPublication"));
			criteria.setProjection(projectionList);

			latestDate = (Date) criteria.uniqueResult();

			if (latestDate == null) {
				return Collections.EMPTY_LIST;
			}

			Query query = session
					.getNamedQuery("Currency.getCurrenciesByDateOfPublication");
			query.setDate("dateOfPublication", latestDate);
			return query.list();

		} finally {
			session.getTransaction().commit();
			session.close();
		}
	}

	@Override
	public void addCurrency(Currency currency) {

		Session session = getSession();

		try {

			session.beginTransaction();
			session.save(currency);

		} finally {
			session.getTransaction().commit();
			session.close();
		}
	}

	@Override
	public void remove(Currency currency) {

		Session session = getSession();

		try {

			session.beginTransaction();
			session.delete(currency);

		} finally {
			session.getTransaction().commit();
			session.close();
		}
	}

	@Override
	public void update(Currency currency) {

		Session session = getSession();

		try {

			session.beginTransaction();
			session.update(currency);

		} finally {
			session.getTransaction().commit();
			session.close();
		}
	}

	@Override
	public void addCurrency(Collection<Currency> currencies) {

		Session session = getSession();

		try {

			session.beginTransaction();
			for (Currency currency : currencies) {
				session.save(currency);
			}

		} finally {
			session.getTransaction().commit();
			session.close();
		}
	}

	@Override
	public Collection<Currency> getCurrencies(Date fromDate, Date toDate) {

		Session session = getSession();

		try {

			session.beginTransaction();

			Query query = session
					.getNamedQuery("Currency.getCurrenciesByDateFromTo");
			query.setDate("fromDate", fromDate);
			query.setDate("toDate", toDate);

			return query.list();

		} finally {
			session.getTransaction().commit();
			session.close();
		}
	}

	@Override
	public Currency getCurrencyByCode(Date dateOfPublication, String codeName) {

		Session session = getSession();

		try {

			session.beginTransaction();

			// "from Currency where dateOfPublication = :dateOfPublication and currencyCode = :codeName"

			Query query = session.getNamedQuery("Currency.getCurrenciesByCode");
			query.setDate("dateOfPublication", dateOfPublication);
			query.setMaxResults(1);
			query.setString("codeName", codeName);

			return (Currency) query.uniqueResult();

		} finally {
			session.getTransaction().commit();
			session.close();
		}
	}

	@Override
	public Currency getCurrencyByName(Date dateOfPublication, String name) {

		Session session = getSession();

		try {

			session.beginTransaction();

			// "from Currency where dateOfPublication = :dateOfPublication and name = :currencyName"

			Query query = session.getNamedQuery("Currency.getCurrenciesByName");
			query.setMaxResults(1);
			query.setDate("dateOfPublication", dateOfPublication);
			query.setString("currencyName", name);

			return (Currency) query.uniqueResult();

		} finally {
			session.getTransaction().commit();
			session.close();
		}
	}

	@Override
	public Collection<Currency> getCurrenciesByKeyWord(Date dateOfPublication,
			String keyWord) {

		Session session = getSession();

		try {

			session.beginTransaction();

			// "from Currency where dateOfPublication = :dateOfPublication and (name like concat('%', :keyWord, '%') or currencyCode like concat('%', :keyWord, '%'))"

			Query query = session
					.getNamedQuery("Currency.getCurrenciesByKeyWord");
			query.setDate("dateOfPublication", dateOfPublication);
			query.setString("keyWord", keyWord);

			return query.list();

		} finally {
			session.getTransaction().commit();
			session.close();
		}
	}

	@Override
	public Collection<Currency> getCurrenciesByValue(Date dateOfPublication,
			BigDecimal minValue) {

		Session session = getSession();

		try {

			session.beginTransaction();

			// "from Currency where dateOfPublication = :dateOfPublication and averageRate >= :minValue"

			Query query = session
					.getNamedQuery("Currency.getCurrenciesByMinValue");
			query.setDate("dateOfPublication", dateOfPublication);
			query.setBigDecimal("minValue", minValue);

			return query.list();

		} finally {
			session.getTransaction().commit();
			session.close();
		}
	}

	@Override
	public Collection<Currency> getCurrenciesByValue(Date dateOfPublication,
			BigDecimal minValue, BigDecimal maxValue) {

		Session session = getSession();

		try {

			session.beginTransaction();

			// "from Currency where dateOfPublication = :dateOfPublication and averageRate >= :minValue and averageRate <= :maxValue"

			Query query = session
					.getNamedQuery("Currency.getCurrenciesByMinAndMaxValue");
			query.setDate("dateOfPublication", dateOfPublication);
			query.setBigDecimal("minValue", minValue);
			query.setBigDecimal("maxValue", maxValue);

			return query.list();

		} finally {
			session.getTransaction().commit();
			session.close();
		}
	}
}
